﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ButtonBuilder
{
    public class AssemblyInfo
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string Product { get; set; }
        public string Company { get; set; }
        public string Copyright { get; set; }
        public string ProductVersion { get; set; }
        public string Version { get; set; }
        public DateTime BuildDate { get; set; }

        public string ToShortString()
        {
            return string.Format("{0} v{1} ({2})",
                Product, ProductVersion, BuildDate.ToString("yyyy/MM/dd"));
        }
    }

    public static class AssemblyUtils
    {
        private static DateTime GetBuildDate(Assembly asm)
        {
            System.Version ver = asm.GetName().Version;
            DateTime buildDate;

            if (ver.Build < 730 || ver.Revision == 0)
            {
                buildDate = System.IO.File.GetLastWriteTime(asm.Location);
            }
            else
            {
                buildDate = new DateTime(2000, 1, 1, 0, 0, 0);
                buildDate = buildDate.AddDays(ver.Build);
                buildDate = buildDate.AddSeconds(ver.Revision * 2);

                if (TimeZone.IsDaylightSavingTime(buildDate, TimeZone.CurrentTimeZone.GetDaylightChanges(buildDate.Year)))
                {
                    buildDate = buildDate.AddHours(1);
                }

                if (buildDate > DateTime.Now || buildDate < new DateTime(2000, 1, 1, 0, 0, 0))
                {
                    buildDate = System.IO.File.GetLastWriteTime(asm.Location);
                }
            }

            return buildDate;
        }

        public static AssemblyInfo GetAssemblyInfo()
        {
            return GetAssemblyInfo(Assembly.GetExecutingAssembly());
        }

        public static AssemblyInfo GetAssemblyInfo(Assembly asm)
        {
            AssemblyInfo info = new AssemblyInfo();
            info.Version = AssemblyName.GetAssemblyName(asm.Location).Version.ToString();
            info.BuildDate = GetBuildDate(asm);

            object[] attributes = asm.GetCustomAttributes(true);

            object attribute = attributes.FirstOrDefault(a => a.GetType() == typeof(AssemblyTitleAttribute));
            if (attribute != null)
            {
                info.Title = ((AssemblyTitleAttribute)attribute).Title;
            }

            attribute = attributes.FirstOrDefault(a => a.GetType() == typeof(AssemblyDescriptionAttribute));
            if (attribute != null)
            {
                info.Description = ((AssemblyDescriptionAttribute)attribute).Description;
            }

            attribute = attributes.FirstOrDefault(a => a.GetType() == typeof(AssemblyProductAttribute));
            if (attribute != null)
            {
                info.Product = ((AssemblyProductAttribute)attribute).Product;
            }

            attribute = attributes.FirstOrDefault(a => a.GetType() == typeof(AssemblyCompanyAttribute));
            if (attribute != null)
            {
                info.Company = ((AssemblyCompanyAttribute)attribute).Company;
            }

            attribute = attributes.FirstOrDefault(a => a.GetType() == typeof(AssemblyCopyrightAttribute));
            if (attribute != null)
            {
                info.Copyright = ((AssemblyCopyrightAttribute)attribute).Copyright;
            }

            attribute = attributes.FirstOrDefault(a => a.GetType() == typeof(AssemblyFileVersionAttribute));
            if (attribute != null)
            {
                info.ProductVersion = ((AssemblyFileVersionAttribute)attribute).Version;
            }

            return info;
        }
    }

    public class PluginInfo
    {
        public string AssemblyPath { get; set; }

        public string ClassName { get; set; }

        public string Description { get; set; }
    }

    public static class PluginUtils
    {
        private static readonly string PLUGIN_INTERFACE = "IPlugin";
        
        public static List<PluginInfo> FindPlugins(string path)
        {
            List<PluginInfo> plugins = new List<PluginInfo>();

            string[] dlls = Directory.GetFileSystemEntries(path, "*.dll");
            foreach (string dllPath in dlls)
            {
                try
                {
                    Assembly dll = Assembly.LoadFrom(dllPath);
                    ExamineAssembly(dll, PLUGIN_INTERFACE, plugins);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                }
            }

            return plugins;
        }

        private static void ExamineAssembly(Assembly objDll, string interfaceName, List<PluginInfo> plugins)
        {
            foreach (Type classType in objDll.GetTypes())
            {
                if (classType.IsPublic)
                {
                    Type classInterface = classType.GetInterface(interfaceName, true);

                    if (classInterface != null)
                    {
                        PluginInfo pi = new PluginInfo()
                        {
                            AssemblyPath = objDll.Location,
                            ClassName = classType.FullName,
                            Description = ((DescriptionAttribute)(Attribute
                                .GetCustomAttribute(classType, typeof(DescriptionAttribute)))).Description
                        };

                        plugins.Add(pi);
                    }
                }
            }
        }

        public static object CreatePlugin(PluginInfo pluginInfo)
        {
            object plugin = null;

            try
            {
                Assembly dll = Assembly.LoadFrom(pluginInfo.AssemblyPath);
                plugin = dll.CreateInstance(pluginInfo.ClassName);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return plugin;
        }
    }
}
